/* Copyright 2011 Rex Kerr and the Howard Hughes Medical Institute, all rights reserved */
/* This file is covered by the BSD license with HHMI as the organization */
/* See http://www.opensource.org/licenses/bsd-license.php for licence template text. */

package org.kerrlab.generators

import org.kerrlab.tools.Utility._

object ShipVLGen {
  sealed abstract class NType(val shortfix: String, val longfix: String) {
    def rational = false
    def asMe(d: Expr): Expr = Expr(
      ( if (d.kind==this) d.text
        else if (rational || !d.kind.rational) "(" + d.text + ").to" + longfix
        else {
          val q = ^(d.kind)
          asMe(NType.rounded( if (q==this || q==d.kind) d else q.asMe(d) )).text
        }
      ),
      this
    )
    def ^(n: NType): NType = {
      (shortfix,n.shortfix) match {
        case ("S",_) => n
        case (_,"S") => this
        case ("I",_) => n
        case (_,"I") => this
        case ("L","L") => this
        case ("L",_) | (_,"L") => DoubleType
        case ("F","F") => this
        case ("F",_) | (_,"F") => DoubleType
        case _ => DoubleType
      }
    }
    def v(n: NType): NType = {
      val t = ^(n)
      if (t==n) this else if (t==this) n else IntType
    }
    def holds(n: NType) = this == (this ^ n)
  }
  object NType {
    def rounded(d: Expr) = d.kind.shortfix match {
      case "F" => Expr("math.round("+d.text+")",IntType)
      case "D" => Expr("math.round("+d.text+")",LongType)
      case _ => d
    }
  }
  object ShortType extends NType("S","Short") { }
  object IntType extends NType("I","Int") { }
  object LongType extends NType("L","Long") { }
  object FloatType extends NType("F","Float") { override def rational = true }
  object DoubleType extends NType("D","Double") { override def rational = true }
  val nums = List(ShortType, IntType, LongType, FloatType, DoubleType)
  
  sealed abstract class RType(val shortfix: String, val longfix: String) {
    def mutates = false
    def supertype: Option[RType]
    def defmods = ""
    def parental = defmods contains "abstract"
    def %(r: RType): RType = {
      shortfix match {
        case "M" | "A" | "B" => r.shortfix match {
          case "M" | "A" | "B" => MutableType
          case _ => ImmutableType
        }
        case _ => ImmutableType
      }
    }
  }
  object GenericType extends RType("G","Generic") { override def defmods = "abstract "; def supertype = None}
  object ImmutableType extends RType("I","Immutable") { override def defmods = "final case "; def supertype = Some(GenericType) }
  object AssignableType extends RType("A","Assignable") { override def mutates = true; override def defmods = "abstract "; def supertype = Some(GenericType) }
  object MutableType extends RType("M","Mutable") { override def mutates = true; override def defmods = "final "; def supertype = Some(AssignableType) }
  object BackedType extends RType("B","Backed") { override def mutates = true; def supertype = Some(AssignableType) }
  val reps = List(GenericType, ImmutableType, AssignableType, MutableType, BackedType)
  val canon = reps.map(r => r -> (if (r==GenericType || r==ImmutableType) ImmutableType else MutableType)).toMap
  
  case class Expr(text: String, kind: NType)(implicit mathless: Implicated[Boolean,Expr] = new Implicated(false,classOf[Expr])) {
    def ^(d: Expr) = kind ^ d.kind
    def as(k: NType) = k.asMe(this)
    def append(s: String) = Expr(text + s, kind)
    def prepend(s: String) = Expr(s + text, kind)
    def mathed = if (kind==ShortType && !mathless) copy(kind=IntType).as(ShortType) else this
    def asmath(k: NType) = (if (kind==ShortType && !mathless) copy(kind=IntType) else this).as(k)
    def mathapp(s: String) = if (s.length==0) this else append(s).mathed
    def mathpre(s: String) = if (s.length==0) this else prepend(s).mathed
  }
  
  val l2n = (('w' to 'z') ++ ('a' to 'w').reverse).map(_.toString).zipWithIndex.drop(1).toMap
  val n2l = l2n.map(_.swap)
  case class Entity(n: Int, m: Int, t: NType, r: RType, id: Caseless[String] = "", mathless: Caseless[Boolean] = false) {
    implicit val implicit_mathless = new Implicated(mathless: Boolean, classOf[Expr])
    def isScalar = (n==1 && m==1)
    def isVector = (n>1 && m==1)
    def isMatrix = (n>1 && m>1)
    def notScalar = (n*m > 1)
    def isVector(k: Int) = (k>1 && n==k && m==1)
    def sameSize(e: Entity) = (n==e.n && m==e.m)
    def mappable(nt: NType) = (notScalar && t != ShortType && nt != ShortType)
    def foldable(nt: NType) = (notScalar && t != ShortType && nt != ShortType)
    
    def tiny = (t.shortfix + r.shortfix + n + (if (isMatrix) m.toString else "")).toLowerCase
    def classname = {
      if (isScalar) "Scalar"+t.shortfix
      else if (isVector) r.shortfix + "Vec" + n + t.shortfix
      else if (n==m) r.shortfix + "Mat" + n + t.shortfix
      else r.shortfix + "Mat" + n + "x" + m + t.shortfix
    }
    def longclassname(user: Boolean = true, uset: Boolean = true) = {
      def tee = if (uset) t.longfix else ""
      def arr = if (user) r.longfix else ""
      if (isScalar) tee + "Scalar"
      else if (isVector) arr + tee + "Vector" + n
      else arr + tee + "Matrix" + n + "x" + m
    }
    def shortclassname(user: Boolean = true, uset: Boolean = true) = {
      def tee = if (uset) t.shortfix else ""
      def arr = if (user) r.shortfix else ""
      if (isScalar) "Scalar"+tee
      else if (isVector) arr + "Vec" + n + tee
      else if (n==m) arr + "Mat" + n + tee
      else arr + "Mat" + n + "x" + m + tee
    }
    def packageclassname = {
      if (isScalar) "scalar."+classname
      else if (isVector) "vector."+classname
      else "matrix."+classname
    }

      
    def defmods = if (isScalar) "final " else r.defmods
    def extension = r.supertype.map(rr => " extends "+copy(r=rr).classname+(if (r==BackedType) " with Iterator["+classname+"]" else "")).getOrElse(" extends Cloneable")
    
    def prefixId(s: String) = id + s
    def param = tiny + ": " + classname
    def paramary = all.map(_.text + "0: " + t.longfix).mkString(", ")
    def withId = copy(id = (tiny+"."))
    def \(i: Int, j: Int, modifier: String=>String = prefixId): Expr = {
      if (isMatrix) Expr(modifier(n2l(i).toString+n2l(j)),t) else Expr(modifier(n2l(i)),t)
    }
    def \\(i: Int, j: Int, t: NType, modifier: String=>String = prefixId): Expr = if (mathless) \(i,j,modifier) else \(i,j,modifier).as(t)
    def ?(i: Int, j: Int, modifier: String=>String = prefixId, nt: NType = t) = if (t==nt) \(i,j,modifier).text else \\(i,j,nt,modifier).text
    def all = for (i <- 1 to n; j <- 1 to m) yield \(i,j)
    def allarg = withId.all
    def allary = for (i <- 1 to n; j <- 1 to m) yield \(i,j,_+"0")
    def ^(e: Entity) = if (mathless) t else t ^ e.t
    def %(e: Entity) = r % e.r
    def byId(nt: NType) = (i: Int, j: Int) => withId.\\(i,j,nt)
    def by0(nt: NType) = (i: Int, j: Int) => \\(i,j,nt,_+"0")
    def assop(op: String, nt: NType) = {
      if (op.length==0 || op.charAt(0).isLetter) { if (t holds nt) op+"Eq" else op+"EqCast" }
      else { if (t holds nt) op+"~" else op+"~~" }
    }
    def overwrite(rt: RType) = if (r==rt) "" else "override "
    
    def define = defmods + "class " + classname + consparams + extension + " {"
    def enddef(ob: Boolean = false) = "} // " + (if (ob) "object" else "class") + " " + classname
    def consparams = r match {
      case GenericType if (isScalar) => all.map("val " + _.text + ": " + t.longfix).mkString("(", ", ", ")")
      case ImmutableType  => all.map("val " + _.text + ": " + t.longfix).mkString("(", ", ", ")")
      case MutableType => all.map("var " + _.text + ": " + t.longfix).mkString("(", ", ", ")")
      case BackedType => "(data: Array["+t.longfix+"])"
      case _ => ""
    }
    def paramethods = r match {
      case GenericType if (n*m>1) => all.map("def " + _.text + ": " + t.longfix).toList
      case AssignableType => all.map(x => "def " + x.text + "_=("+x.text+"0: " + t.longfix + "): Unit").toList
      case BackedType => (for ((x,i) <- all.zipWithIndex) yield List(
        "def " + x.text + " = data(index+"+i+")",
        "def " + x.text + "_=("+x.text+"0: " + t.longfix + ") { data(index+"+i+") = "+x.text+"0 }"
      )).toList.flatten.:::(List("var index = 0","private var fresh = true"))
      case _ => Nil
    }
    def overmethods = r match {
      case GenericType => List(
        "override def toString = " + 
          (1 to n).map(i => (1 to m).map(j => ?(i,j)).mkString("""+", "+""")).mkString(""""["+""", (if (m==1) """+", "+""" else """+" ; "+"""), """+"]""""),
        "override def hashCode = " + all.map(_.text+(if ((t^IntType) == IntType) "" else ".##")).reduceRight((l,r) => "("+r+")*16777619 ^ "+l),
        "override def equals(any: Any) = any match { " + nums.map(nt => {
          val o = copy(t = nt).withId
          "case "+o.param+" => {"+(all,o.allarg).zipped.map(_.text + "==" + _.text).mkString(" && ")+" }"
        }).mkString("","; ","; case _ => false }"),
        "override def clone(): "+classname+" = "+copy(r=canon(r)).create( (i,j) => \(i,j) )
      )
      case ImmutableType => List(
        "override def hashCode = super.hashCode",
        "override def equals(any: Any) = super.equals(any)",
        "override def clone(): "+classname+" = "+create( (i,j) => \(i,j) )
      )
      case AssignableType => List(
        "override def clone(): "+classname+" = "+copy(r=canon(r)).create( (i,j) => \(i,j) )
      )
      case MutableType => List(
        "override def clone(): "+classname+" = "+create( (i,j) => \(i,j) )
      )
      case BackedType => List(
        "override def toString = if (index < 0 || index+"+(n*m)+" > data.length) (\"(out of range)\") else (" +
          (1 to n).map(i => (1 to m).map(j => \(i,j).text).mkString("""+", "+""")).mkString(""""["+""", """+" ; "+""", """+"]"""") + ")",
        "def hasNext = (fresh && "+(n*m)+" <= data.length) || (index + " + (2*n*m) + " <= data.length)",
        "def next = { if (fresh) fresh = false else index += " + (n*m) + "; this }",
        "def reset(idx: Int = 0) = { index = idx*"+(n*m)+"; fresh = true; this }",
        "def spawn = { val v = new "+classname+"(data); v.index = index; v.fresh = fresh; v }",
        "override def duplicate = (this,spawn)"
      )
      case _ => Nil
    }

    def defcompanion = "object " + classname + " {"
    def companionmethods = {
      def rotor(s: String) = "{ val c = "+Expr("math.cos(theta)",DoubleType).as(t).text + "; val s = "+Expr("math.sin(theta)",DoubleType).as(t).text+"; "+s+" }"
     
      if (isScalar) {
        if (r!=GenericType || !t.rational) Nil
        else List(
          "def close(a: "+t.longfix+", b: "+t.longfix+", eps: "+t.longfix+") = eps*math.max(1.0,a*a+b*b) >= (a-b)*(a-b)"
        )
      }
      else {
        (r match {
          case ImmutableType => List(
            "val pattern = " + (1 to n).map(i => 
              (1 to m).map(j => "([0-9eE+.-]+)").mkString("""\s*,\s*""")
            ).mkString("\"\"\"\\[","""\s*"""+(if (m==1) "[,;]" else ";")+"""\s*""","\\]\"\"\".r"),
            "def parse(str: String): "+classname + 
              " = { val p = pattern.unapplySeq(str).getOrElse(throw new NumberFormatException).toArray.map(_.to"+t.longfix+"); " +
              create((i,j) => Expr("p("+((i-1)*m+(j-1))+")", t))+" }"
          )
          case BackedType => List(
            "def apply(a0: Array["+t.longfix+"]) = new "+classname+"(a0)",
            "def apply(i0: Int) = new "+classname+"(new Array["+t.longfix+"](i0*"+(n*m)+"))"
          )
          case _ => List(
            "def apply(" + paramary + "): "+ classname + " = " + copy(r=canon(r)).create(by0(t)),
            "def parse(str: String): " + classname + " = " + copy(r=ImmutableType).classname + ".parse(str)" + (if (canon(r)==ImmutableType) "" else ".mut")
          )
        }) ::: (this match {
          case Entity(2,1,_,_,_,_) if (t.rational && r!=BackedType) => List(
            "def polar(r0: "+t.longfix+", theta: "+t.longfix+"): "+classname+" = "+copy(r=canon(r)).create((i,j) => (i,j) match {
              case (1,1) => Expr("r0 * math.cos(theta)",DoubleType).as(t)
              case (2,1) => Expr("r0 * math.sin(theta)",DoubleType).as(t)
            })
          )
          case Entity(3,1,_,_,_,_) if (t.rational && r!=BackedType) => List(
            "def spherical(r0: "+t.longfix+", phi: "+t.longfix+", theta: "+t.longfix+"): "+classname +
            " = { val rsp = r0*math.sin(phi); "+copy(r=canon(r)).create((i,j) => (i,j) match {
              case (1,1) => Expr("rsp*math.cos(theta)",DoubleType).as(t)
              case (2,1) => Expr("rsp*math.sin(theta)",DoubleType).as(t)
              case (3,1) => Expr("r0*math.cos(phi)",DoubleType).as(t)
            }) + " }",
            "def cylindrical(r0: "+t.longfix+", theta: "+t.longfix+", z0: "+t.longfix+"): "+classname+" = " + copy(r=canon(r)).create((i,j) => (i,j) match {
              case (1,1) => Expr("r0 * math.cos(theta)",DoubleType).as(t)
              case (2,1) => Expr("r0 * math.sin(theta)",DoubleType).as(t)
              case (3,1) => Expr("z0",t)
            })
          )
          case Entity(2,2,_,_,_,_) if (t.rational && r!=BackedType) => List(
            "def rotation(theta: "+t.longfix+") = " + rotor(copy(r=canon(r)).create((i,j) => Expr( if (i==j) "c" else if (i>j) "s" else "-s", DoubleType )))
          )
          case Entity(3,3,_,_,_,_) if (t.rational && r!=BackedType) => 
            val e = copy(r=canon(r))
            def about(K: Int) = (i: Int, j: Int) => (i,j) match {
              case (K,K) => Expr("1",t)
              case (K,_) | (_,K) => Expr("0",t)
              case _ => Expr( if (i==j) "c" else if ((K!=2)==(i>j)) "s" else "-s", DoubleType )
            }
            List(
              "def xRotation(theta: "+t.longfix+") = "+rotor(e.create(about(1))),
              "def yRotation(theta: "+t.longfix+") = "+rotor(e.create(about(2))),
              "def zRotation(theta: "+t.longfix+") = "+rotor(e.create(about(3))),
              "def rotation(axis: "+copy(m=1,r=GenericType).classname+", theta: "+t.longfix+") = "+rotor(
                "val fact = 1/axis.len; " + e.create((i,j) => Expr( (i,j) match {
                  case (1,1) => "c + axis.x*axis.x*fact*fact*(1-c)"
                  case (1,2) => "axis.x*axis.y*fact*fact*(1-c)-axis.z*fact*s"
                  case (1,3) => "axis.x*axis.z*fact*fact*(1-c)+axis.y*fact*s"
                  case (2,1) => "axis.x*axis.y*fact*fact*(1-c)+axis.z*fact*s"
                  case (2,2) => "c + axis.y*axis.y*fact*fact*(1-c)"
                  case (2,3) => "axis.y*axis.z*fact*fact*(1-c)-axis.x*fact*s"
                  case (3,1) => "axis.x*axis.z*fact*fact*(1-c)-axis.y*fact*s"
                  case (3,2) => "axis.y*axis.z*fact*fact*(1-c)+axis.x*fact*s"
                  case (3,3) => "c + axis.z*axis.z*fact*fact*(1-c)"
                }, DoubleType ))
              )
            )
          case _ => Nil
        }) :::
        ( if (isVector) r match {
            case ImmutableType =>
              ("val zero = "+create((i,j) => Expr("0",t))) ::
              (1 to n).map(i => "val "+n2l(i)+"Unit = "+create((a,b) => Expr(if (a==i) "1" else "0",t))).toList
            case GenericType =>
              ("val zero: " + classname + " = " + copy(r=ImmutableType).classname + ".zero") ::
              (1 to n).map(i => "val "+n2l(i)+"Unit: "+classname+" = "+copy(r=ImmutableType).classname+"."+n2l(i)+"Unit").toList
            case MutableType =>
              ("def zero = "+create((i,j) => Expr("0",t))) ::
              (1 to n).map(i => "def "+n2l(i)+"Unit = "+create((a,b) => Expr(if (a==i) "1" else "0",t))).toList
            case AssignableType =>
              ("def zero: " + classname + " = " + copy(r=MutableType).classname + ".zero") ::
              (1 to n).map(i => "def "+n2l(i)+"Unit: "+classname+" = "+copy(r=MutableType).classname+"."+n2l(i)+"Unit").toList
            case _ => Nil
          }
          else if (isMatrix) r match {
            case ImmutableType => List(
              "val identity = "+create((i,j) => Expr(if (i==j) "1" else "0",t)),
              "val zero = "+create((i,j) => Expr("0",t)),
              copy(m=1,r=GenericType).withId.use(e => "def traced("+e.param+") = "+create((i,j) => Expr(if (i==j) e?(i,1) else "0",t)))
            )
            case GenericType => List(
              "val identity: " + classname + " = " + copy(r=ImmutableType).classname + ".identity",
              "val zero: " + classname + " = " + copy(r=ImmutableType).classname + ".zero",
              copy(m=1,r=GenericType).withId.use(e => "def traced("+e.param+") = "+copy(r=canon(r)).create((i,j) => Expr(if (i==j) e?(i,1) else "0",t)))
            )
            case MutableType => List(
              "def identity = "+create((i,j) => Expr(if (i==j) "1" else "0",t)),
              "def zero = "+create((i,j) => Expr("0",t)),
              copy(m=1,r=GenericType).withId.use(e => "def traced("+e.param+") = "+create((i,j) => Expr(if (i==j) e?(i,1) else "0",t)))
            )
            case AssignableType => List(
              "def identity: " + classname + " = " + copy(r=MutableType).classname + ".identity",
              "def zero: " + classname + " = " + copy(r=MutableType).classname + ".zero",
              copy(m=1,r=GenericType).withId.use(e => "def traced("+e.param+") = "+copy(r=canon(r)).create((i,j) => Expr(if (i==j) e?(i,1) else "0",t)))
            )
            case _ => Nil
          }
          else Nil
        )
      }
    }
    
    def implicitconversions = {
      (
        if (isScalar) {
          if (r!=GenericType) Nil
          else List(
            "implicit def primitive_to_"+classname.toLowerCase+"(x0: "+t.longfix+") = new "+classname+"(x0)",
            "implicit def "+classname.toLowerCase+"_to_primitive(vec: "+classname+") = vec.x"
          )
        }
        else if (r!=ImmutableType) Nil
        else if (isVector) List(
          "implicit def tuple_to_"+classname.toLowerCase+"(tup: ("+(1 to n).map(_=>t.longfix).mkString(",")+")) = " +
            "new "+classname+(1 to n).map("tup._"+_).mkString("(",",",")"),
          "implicit def "+classname.toLowerCase+"_to_tuple(vec: "+classname+") = "+copy(id="vec.").all.map(_.text).mkString("(",",",")")
        )
        else List(
          "implicit def tuptup_to_"+classname.toLowerCase+"(tt: "+
            (1 to n).map(_=>(1 to m).map(_=>t.longfix).mkString("(",",",")")).mkString("(",", ",")")+") = new "+classname+
            (1 to n).map(i=>(1 to m).map(j=>"tt._"+i+"._"+j).mkString(",")).mkString("(",", ",")"),
          "implicit def "+classname.toLowerCase+"_to_tuptup(mat: "+classname+") = "+
            copy(id="mat.").use(e => (1 to n).map(i=>(1 to m).map(j=>e.\(i,j).text).mkString("(",",",")")).mkString("(",", ",")"))
        )
      ) ::: (this match {
        case Entity(2,1,IntType,ImmutableType,_,_) => List(
          "implicit def point_to_"+classname.toLowerCase+"(point: java.awt.Point) = new "+classname+"(point.x, point.y)",
          "implicit def dimension_to_"+classname.toLowerCase+"(dim: java.awt.Dimension) = new "+classname+"(dim.width,dim.height)"
        )
        case Entity(2,1,IntType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_point(vec: "+classname+") = new java.awt.Point(vec.x, vec.y)",
          "implicit def "+classname.toLowerCase+"_to_dimension(vec: "+classname+") = new java.awt.Dimension(vec.x, vec.y)"
        )
        case Entity(2,1,FloatType,ImmutableType,_,_) => List(
          "implicit def point2df_to_"+classname.toLowerCase+"(point: java.awt.geom.Point2D.Float) = new "+classname+"(point.x, point.y)"
        )
        case Entity(2,1,FloatType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_point2df(vec: "+classname+") = new java.awt.geom.Point2D.Float(vec.x, vec.y)"
        )
        case Entity(2,1,DoubleType,ImmutableType,_,_) => List(
          "implicit def point2d_to_"+classname.toLowerCase+"(point: java.awt.geom.Point2D) = new "+classname+"(point.getX, point.getY)",
          "implicit def dimension2d_to_"+classname.toLowerCase+"(dim: java.awt.geom.Dimension2D) = new "+classname+"(dim.getWidth, dim.getHeight)"
        )
        case Entity(2,1,DoubleType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_point2d(vec: "+classname+") = new java.awt.geom.Point2D.Double(vec.x, vec.y)",
          "implicit def "+classname.toLowerCase+"_to_dimension2d(vec: "+classname+") = " + 
            "new java.awt.geom.Dimension2D { private[this] var width = vec.x; private[this] var height = vec.y; " + 
            "def getWidth = width; def getHeight = height; def setSize(w: Double, h: Double) { width=w; height=h } }"
        )
        case Entity(2,2,IntType,ImmutableType,_,_) => List(
          "implicit def rect_to_"+classname.toLowerCase+"(rect: java.awt.Rectangle) = new "+classname+"(rect.x, rect.x+rect.width, rect.y, rect.y+rect.height)"
        )
        case Entity(2,2,IntType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_rect(mat: "+classname+") = new java.awt.Rectangle(mat.xx, mat.yx, mat.xy-mat.xx, mat.yy-mat.yx)"
        )
        case Entity(2,2,FloatType,ImmutableType,_,_) => List(
          "implicit def rect2df_to_"+classname.toLowerCase+"(rect: java.awt.geom.Rectangle2D.Float) = " +
            "new "+classname+"(rect.x, rect.x+rect.width, rect.y, rect.y+rect.height)"
        )
        case Entity(2,2,FloatType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_rect2df(mat: "+classname+") = " +
            "new java.awt.geom.Rectangle2D.Float(mat.xx, mat.yx, mat.xy-mat.xx, mat.yy-mat.yx)"
        )
        case Entity(2,2,DoubleType,ImmutableType,_,_) => List(
          "implicit def rectshape_to_"+classname.toLowerCase+"(rect: java.awt.geom.RectangularShape) = " +
            "new "+classname+"(rect.getX, rect.getX+rect.getWidth, rect.getY, rect.getY+rect.getHeight)"
        )
        case Entity(2,2,DoubleType,GenericType,_,_) => List(
          "implicit def "+classname.toLowerCase+"_to_rect2d(mat: "+classname+") = " +
            "new java.awt.geom.Rectangle2D.Double(mat.xx, mat.yx, mat.xy-mat.xx, mat.yy-mat.yx)"
        )
        case Entity(3,3,_,GenericType,_,_) if (t.rational) => List(
          "implicit def "+classname.toLowerCase+"_to_affine(vec: "+classname+") = " +
            "new java.awt.geom.AffineTransform("+copy(id="vec.").all.take(6).map(_.text).mkString(",")+")"
        )
        case Entity(3,3,DoubleType,ImmutableType,_,_) => List(
          "implicit def affine_to_"+classname.toLowerCase+"(af: java.awt.geom.AffineTransform) = " +
            "{ val ar = new Array[Double](6); af.getMatrix(ar); new "+classname+(1 to 6).map("ar("+_+")").mkString("(",", ",", 0, 0, 1)")+" }"
        )
        case _ => Nil
      })
    }

    def create(f: (Int,Int) => Expr) = {
      "new "+classname+"("+(for (i <- 1 to n; j <- 1 to m) yield f(i,j).as(t).text).mkString(", ")+")"
    }
    def assign(f: (Int,Int) => Expr) = {
      (for (i <- 1 to n; j <- 1 to m) yield (?(i,j) + " = " + f(i,j).as(t).text)).mkString("; ")
    }
    def doit(f: (Int,Int) => Expr, make: Boolean = false) = if (make) create(f) else "{ " +assign(f)+"; this }"
    
    def opCopy = List(
      onlyIf (r != BackedType) {
        overwrite(GenericType) + "def copy(" + (paramary.split(", "),all.map(_.text)).zipped.map(_ + " = " + _).mkString(", ") + "): "+classname+" = " +
        copy(r=canon(r)).create((i,j) => \(i,j,_+"0"))
      },
      onlyIf (r != BackedType && n>2) {
        val (nn,mm) = (n-1, 1 max (m-1))
        overwrite(GenericType)+"def trim: "+copy(n=nn,m=mm).classname+" = new "+
        copy(n=nn,m=mm,r=canon(r)).classname+(for (i <- 1 to nn; j <- 1 to mm) yield \(i,j).text).mkString("(",", ",")")
      },
      onlyIf (r != BackedType && ((n<veclen && isVector) || (m<matlen && isMatrix))) {
        val (nn,mm) = (n+1, if (isVector) 1 else m+1)
        overwrite(GenericType)+"def pad(padding: "+t.longfix+" = 1): "+copy(n=nn,m=mm).classname+" = "+
        copy(n=nn,m=mm,r=canon(r)).create((i,j) => if (i<=n && j<=m) \(i,j) else Expr("padding",t))
      },
      onlyIf (r == ImmutableType && isVector) { "def vec = this" },
      onlyIf (r == ImmutableType && isMatrix) { "def mat = this" }
    ).flatten
    def opTo(nt: NType) = List(
      onlyIf (t != nt && !(n==1 && m==1)) {
        overwrite(GenericType) + "def to" + nt.shortfix + ": " + (if (r==BackedType) copy(t=nt,r=MutableType) else copy(t=nt)).classname + " = " +
          copy(t = nt, r = canon(r)).create((i,j) => \\(i,j,nt))
      },
      onlyIf (mappable(nt) && r==GenericType) {
        "def map" + nt.shortfix +"(fun: "+t.longfix+" => "+nt.longfix+") = "+copy(t=nt, r=canon(r)).create((i,j) => \\(i,j,nt,"fun("+_+")"))
      },
      onlyIf (mappable(nt) && r==AssignableType && t==nt) {
        "def mapEq(fun: "+t.longfix+" => "+t.longfix+"): this.type = {"+copy(r=canon(r)).assign((i,j) => \(i,j,"fun("+_+")"))+"; this }"
      }
    ).flatten
    def opCast(rt: RType) = (onlyIf (notScalar) { r match {
      case GenericType => rt match {
        case ImmutableType => Some("def imm = " + copy(r=rt).create(\ (_,_)))
        case MutableType => Some("def mut = " + copy(r=rt).create(\ (_,_)))
        case _ => None
      }
      case ImmutableType => rt match {
        case ImmutableType => Some("override def imm = this")
        case _ => None
      }
      case _ => None
    } }.getOrElse(None)).toList
    def opSolo(nt: NType, rt: RType) = (if (t==nt && r==rt) opCopy else Nil) ::: (if (r==rt) opTo(nt) else Nil) ::: (if (t==nt) opCast(rt) else Nil)
    
    def uniReduce(meth: String, pre: String => String, coll: (String,String) => String, wrap: Expr=>Expr = identity) = {
      onlyIf (notScalar && r==GenericType) {
        "def "+meth+" = " + wrap(Expr((for (i <- 1 to n; j <- 1 to m) yield pre(\(i,j).text)).reduceLeft(coll), t)).text
      }
    }
    def uniLenSq = onlyIf (isVector) { uniReduce("lenSq", s => s+"*"+s, (a,b) => a+" + "+b) }.getOrElse(None)
    def uniLen = onlyIf(isVector && t.rational && r==GenericType) { "final def len = " + Expr("math.sqrt(lenSq)", DoubleType).as(t).text }
    def uniMakesScalar = List(uniLenSq,uniLen).flatten ::: (this match {
      case Entity(2,1,_,GenericType,_,_) if (t.rational) => List(
        "def theta = "+Expr("math.atan2(y,x)",DoubleType).as(t).text
      )
      case Entity(3,1,_,GenericType,_,_) if (t.rational) => List(
        "def inclination = "+Expr("math.acos(z/math.sqrt(x*x + y*y + z*z))",DoubleType).as(t).text,
        "def azimuth = "+Expr("math.atan2(y,x)",DoubleType).as(t).text        
      )
      case _ => Nil
    })
    def uniMakesUni = {
      (this match {
        case Entity(n,m,_,GenericType,_,_) if (notScalar) => List(
          "def unary_-() = "+copy(r=canon(r)).create((i,j) => \(i,j,"-"+_).mathed)
        )
        case Entity(n,m,_,AssignableType,_,_) if (notScalar) => List(
          "def negEq: this.type = { " + all.map(a => a.text + " = " + a.mathpre("-").text).mkString("; ") + "; this }"
        )
        case _ => Nil
      }) ::: (this match {
        case Entity(2,1,_,GenericType,_,_) => List(
          "def cw = "+copy(r=canon(r)).create((i,j) => \(3-i,j) mathpre (if (i==2) "-" else "")),
          "def ccw = "+copy(r=canon(r)).create((i,j) => \(3-i,j) mathpre (if (i==1) "-" else ""))
        )
        case Entity(2,1,_,AssignableType,_,_) => List(
          "def cwEq: this.type = { val x0 = y; y = " + (\(1,1) mathpre "-").text + "; x = x0; this }",
          "def ccwEq: this.type = { val x0 = " + (\(2,1) mathpre "-").text + "; y = x; x = x0; this }"
        )
        case _ => Nil
      }) ::: (this match {
        case Entity(_,1,_,GenericType,_,_) if (isVector && t.rational) => List(
          "def norm = { val k0 = 1/this.len; "+copy(r=canon(r)).create((i,j) => \(i,j) mathapp ("*k0")) + " }"
        )
        case Entity(_,1,_,AssignableType,_,_) if (isVector && t.rational) => List(
          "def normEq: this.type = { val k0 = 1/this.len; "+all.map(a => a.text + " = " + a.mathapp("*k0").text).mkString("; ")+"; this }"
        )
        case _ => Nil
      })
    }
    def uniAll = uniMakesScalar ::: uniMakesUni
    
    def opOverElement(meth: String, e: Entity, op: (String,String) => String) = {
      if (isScalar && e.isScalar) None
      else onlyIf (meth!=":" && e.isScalar && r==GenericType && e.r==GenericType && ((t^e.t)==e.t || (t==FloatType && e.t==LongType))) {
        "def "+meth+"("+e.paramary+") = "+Entity(n, m, ^(e), %(e)).create((i,j) => Expr( op(\\(i,j,^(e)).text, e.\\(1,1,^(e),_+"0").text) , ^(e) ).mathed)      
      }
    }
    def eqOverElement(meth: String, e: Entity, op: (String,String) => String) = {
      onlyIf(e.isScalar && r==AssignableType && e.r==GenericType && ((t^e.t)==e.t || (t==FloatType && e.t==LongType))) {
        "def " + assop(meth,e.t) + "(" + e.paramary + "): this.type = { " + (
          if (meth == ":") all.map(a => a.text + " = " + e.?(1,1,_+"0",t)).mkString("; ")
          else (for (i <- 1 to n; j <- 1 to m) yield (?(i,j) + " = " + Expr( op(?(i,j,nt = ^(e)), e?(1,1,_+"0",^(e))), ^(e) ).mathed.as(t).text)).mkString("; ")
        ) + "; this }"
      }
    }
    def opByElement(meth: String, e: Entity, op: (String,String) => String, par: String, arg: (Int,Int)=>Expr) = {
      onlyIf (meth!=":" && ((notScalar && sameSize(e)) || (isScalar && e.notScalar)) && r==GenericType && e.r==GenericType) {
        "def "+meth+"("+par+") = "+Entity(n max e.n, m max e.m, ^(e), %(e)).create((i,j) => Expr(op(?(i min n,j min m,nt = ^(e)), arg(i,j).text) , ^(e)).mathed)
      }
    }
    def eqByElement(meth: String, e: Entity, op: (String,String) => String, par: String, arg: (Int,Int)=>Expr) = {
      onlyIf (((notScalar && sameSize(e)) || (isScalar && e.notScalar)) && r==AssignableType && e.r==GenericType) {
        "def " + assop(meth,e.t) + "(" + par + "): this.type = { " + (
          if (meth == ":") (for (i <- 1 to n; j <- 1 to m) yield ( \(i,j).text + " = " + arg(i,j).as(t).text )).mkString("; ")
          else (for (i <- 1 to n; j <- 1 to m) yield (?(i,j)+" = "+Expr(op(?(i min n,j min m,nt = ^(e)),arg(i,j).text), ^(e)).mathed.as(t).text)).mkString("; ")
        ) + "; this }"
      }
    }
    def opByTrio(meth: String, e: Entity, op: (String,String) => String) = (
      List(opOverElement(meth,e,op),eqOverElement(meth,e,op), opByElement(meth,e,op,e.param,e.byId(^(e))), eqByElement(meth,e,op,e.param,e.byId(^(e)))) :::
      (if (t==e.t && notScalar) List(opByElement(meth,e,op,e.paramary,e.by0(^(e))), eqByElement(meth,e,op,e.paramary,e.by0(^(e)))) else Nil)
    ).flatten
    def opSubCross(i: Int, u: (Int,Int) => Expr, v: (Int,Int) => Expr, nt: NType) = {
      val j = (i%3)+1
      val k = (j%3)+1
      Expr(u(j,1).text+"*"+v(k,1).text + " - " + u(k,1).text+"*"+v(j,1).text,nt)
    }
    def opCrossProduct(meth: String, e: Entity, par: String, arg: (Int,Int)=>Expr) = onlyIf (isVector(3) && sameSize(e) && r==GenericType && e.r==GenericType) {
      "def " + meth + "(" + par + ") = " + Entity(n, m, ^(e), %(e)).create((i,j) => opSubCross(i, (a,b) => \\(a,b,^(e)), arg, ^(e)).mathed)
    }
    def eqCrossProduct(meth: String, e: Entity, par: String, arg: (Int,Int)=>Expr) = {
      onlyIf (isVector(3) && sameSize(e) && r==AssignableType && e.r==GenericType) {
        "def " + assop(meth,e.t) + "(" + par + "): this.type = { " + {
          val calc = (1 to 3).map(opSubCross(_, (a,b) => \\(a,b,^(e)), arg, ^(e)).mathed)
          "val x0 = " + calc(0).as(t).text + "; val y0 = " + calc(1).as(t).text + "; z = " + calc(2).as(t).text
        } + "; x = x0; y = y0; this }"
      }
    }
    def opOperate(e: Entity) = onlyIf (notScalar && sameSize(e) && e.r==GenericType && (r==GenericType || r==AssignableType)) {
      val ee = e.withId
      if (r==GenericType) {
        "def operate("+e.param+")(fun: ("+(^(e)).longfix+","+(^(e)).longfix+")=>"+(^(e)).longfix+") = " +
          copy(r=canon(r),t=(^(e))).create( (i,j) => Expr("fun("+(\\(i,j,^(e))).text+","+(ee\\(i,j,^(e))).text+")",^(e)).mathed )
      }
      else {
        "def "+assop("operate",^(e))+"("+e.param+")(fun: ("+(^(e)).longfix+","+(^(e)).longfix+")=>"+(^(e)).longfix+") = { " +
          assign( (i,j) => Expr("fun("+(\\(i,j,^(e))).text+","+(ee\\(i,j,^(e))).text+")",^(e)).mathed.as(t) ) + "; this }"
      }
    }.toList
    
    def opColon(e: Entity) = opByTrio(":", e, (a,b) => b)
    def opPlus(e: Entity) = opByTrio("+", e, (a,b) => a+" + "+b)
    def opMinus(e: Entity) = opByTrio("-", e, (a,b) => a+" - "+b)
    def opTimes(e: Entity) = opByTrio("*", e, (a,b) => a+" * "+b)
    def opDivide(e: Entity) = if (isScalar) Nil else opByTrio("/", e, (a,b) => a+" / "+b)
    def opCross(e: Entity) = List(opCrossProduct("cross", e, e.param, e.byId(^(e))), eqCrossProduct("cross", e, e.param, e.byId(^(e)))).flatten
    def opMax(e: Entity) = opByTrio("max", e, (a,b) => "math.max("+a+","+b+")")
    def opMin(e: Entity) = opByTrio("min", e, (a,b) => "math.min("+a+","+b+")")
    
    def opArith(es: List[Entity]) = List(opColon _, opPlus _, opMinus _, opTimes _, opDivide _, opCross _, opMax _, opMin _, opOperate _).map(opx => es.map(opx).flatten).flatten
    
    def opReduce(
      meth: String, e: Entity, op: (String,String) => String, coll: (String,String) => String, par: String, arg: (Int,Int) => Expr, wrap: Expr=>Expr = identity
    ) = {
      onlyIf (sameSize(e) && notScalar && r==GenericType && e.r==GenericType) {
        "def " + meth + "(" + par + ") = " + wrap(
          (for (i <- 1 to n; j <- 1 to m) yield Expr( op(\\(i,j,^(e)).text, arg(i,j).text) , ^(e) ).mathed).reduceLeft((p,q) =>
            Expr( coll( p.as(p^q).text, q.as(p^q).text ) , p^q ).mathed
          )
        ).text
      }
    }
    def opReduceAlias(newmeth: String, oldmeth: String, e: Entity, aryied: Boolean, wrap: Expr => Expr = identity) = {
      onlyIf(sameSize(e) && notScalar && r==GenericType && e.r==GenericType) {
        "final def " + newmeth + "(" + (if (aryied) e.paramary else e.param) + "): " + (^(e)).longfix + " = " +
        wrap( Expr("this."+oldmeth+"("+(if (aryied) e.allary.map(_.text).mkString(", ") else e.tiny)+")",^(e)) ).text
      }
    }
    
    def opDot(e: Entity, par: String, arg: (Int,Int)=>Expr) = onlyIf(isVector) { opReduce("dot", e, (a,b)=>a+"*"+b, (a,b)=>a+" + "+b, par, arg) } getOrElse None
    def opDotAlias(e: Entity, aried: Boolean) = onlyIf (isVector) { opReduceAlias("**", "dot", e, aried) } getOrElse None
    def opUnitDot(e: Entity) = onlyIf (isVector && ^(e).rational) {
      opReduceAlias("unitDot", "dot", e, false, p => Expr(p.text + " / math.sqrt(lenSq * "+e.tiny+".lenSq)", DoubleType).as(^(e)))
    }.getOrElse(None)
    def opUnitDotAlias(e: Entity) = onlyIf (isVector && ^(e).rational) { opReduceAlias("*^*", "unitDot", e, false) } getOrElse None
    def opRotDot(e: Entity, par: String, arg: (Int,Int)=>Expr) = onlyIf (isVector && n==2) {
      opReduce("rotdot", e, (a,b) => a+"*"+b, (a,b) => a + " - " + b, par, (i,j)=>arg(3-i,j))
    }.getOrElse(None)
    def opRotDotAlias(e: Entity, aried: Boolean) = onlyIf(isVector && n==2) { opReduceAlias("X", "rotdot", e, aried) } getOrElse None
    
    def opDistSq(e: Entity, par: String, arg: (Int,Int)=>Expr) = onlyIf (isVector) {
      opReduce("distSq", e, (a,b) => "(%s-%s)*(%s-%s)".format(a,b,a,b), (a,b) => a + " + " + b, par, arg)
    }.getOrElse(None)
    def opDist(e: Entity, aried: Boolean) = onlyIf(isVector) {
      opReduceAlias("dist", "distSq", e, aried, p => Expr("math.sqrt("+p.text+")",DoubleType).as(^(e)))
    }.getOrElse(None)
    
    def opEquality(e: Entity, par: String, arg: (Int,Int)=>Expr) = {
      onlyIf (notScalar && sameSize(e) && r==GenericType && e.r==GenericType && t==e.t) { 
        copy(mathless=true).opReduce("===", e.copy(mathless=true), _+"=="+_, _+" && "+_, par, arg) 
      } getOrElse None
    }
    def opApproximate(e: Entity, par: String, arg: (Int,Int)=>Expr) = {
      onlyIf (sameSize(e) && r==GenericType && e.r==GenericType && t.rational && e.t.rational) {
        val scalar = copy(n=1,m=1,t=t^e.t)
        copy(mathless=true).opReduce("approx", e, scalar.classname+".close("+_+","+_+",eps)", _+" && "+_, par+", eps: "+scalar.t.longfix, arg)
      }.getOrElse(None)
    }
    def opApproximateAlias(e: Entity, aried: Boolean) = onlyIf (!aried && sameSize(e) && r==GenericType && r==e.r && t.rational && e.t.rational && notScalar) {
      val error = (t v e.t) match {
        case FloatType => "1e-10" + (if ((t^e.t)==FloatType) "f" else "")
        case _ => "1e-20"
      }
      "final def =~=("+e.param+") = approx("+e.tiny+", "+error+")"
    }
    def opDoFold(e: Entity) = onlyIf (foldable(e.t) && sameSize(e) && r==GenericType && e.r==GenericType) {
      "def fold(zero: "+e.t.longfix+")(fun: ("+e.t.longfix+","+t.longfix+")=>"+e.t.longfix+") = "+("zero" /: all.map(_.text))((a,b) => "fun("+a+","+b+")")
    }
    def opDoReduce(e: Entity) = onlyIf(foldable(e.t) && sameSize(e) && t==e.t && r==GenericType && e.r==GenericType) {
      "def reduce(fun: ("+t.longfix+","+t.longfix+")=>"+t.longfix+") = "+Expr(all.map(_.text).reduceLeft((a,b)=>"fun("+a+","+b+")"),t).mathed.text
    }
    def opOuter(e: Entity) = onlyIf (isVector && sameSize(e) && r==GenericType && e.r==GenericType && n<matlen) {
      val ee = e.withId
      "def outer("+e.param+") = "+Entity(n,n,^(e),canon(r)).create((i,j) => Expr( ?(i,1)+"*"+ee?(j,1) , ^(e) ).mathed)
    }
    
    def opScalize(f1: (Entity, String, (Int,Int)=>Expr)=>Option[String], f2: (Entity, Boolean) => Option[String]) = List[Entity => Option[String]](
      e => f1(e, e.param, e.byId(^(e))),
      e => f2(e, false),
      e => if (t==e.t) f1(e,e.paramary,e.by0(^(e))) else None,
      e => if (t==e.t) f2(e,true) else None      
    )

    def opMakesScalar(es: List[Entity]) = {
      (
        opScalize(opDot, opDotAlias) ::: List(opUnitDot _, opUnitDotAlias _, opDoFold _, opDoReduce _, opOuter _) :::
        opScalize(opRotDot, opRotDotAlias) ::: opScalize(opDistSq,opDist) :::
        opScalize(opEquality, (a,b) => None) ::: opScalize(opApproximate, opApproximateAlias)
      ).map( opx => es.map(opx).flatten ).flatten
    }
    
    def det(k: Int, v: (Int,Int)=>String): String = {
      if (k<=1) v(1,1)
      else if (k==2) v(1,1)+"*"+v(2,2)+" - "+v(1,2)+"*"+v(2,1)
      else (1 to k).map(i => v(1,i)+"*("+det(k-1, (a,b) => v(a+1, if (b+i>k) b+i-k else b+i))+")").mkString(" + ")
    }
    def after(i: Int, j: Int): (Int,Int) => String = (a,b) => ?(if (a+i>n) a+i-n else a+i, if (b+j>m) b+j-n else b+j)
    
    def matMods = onlyIf (isMatrix && r==GenericType) {
      List(
        "def det = "+Expr(det(n, (a,b) => ?(a,b)),t).mathed.text,
        "def trace = "+copy(m=1,r=canon(r)).create((i,j) => \(i,i))
      ) :::
      (1 to n).toList.flatMap(i => List(
        "def "+n2l(i)+"Row = "+copy(m=1,r=canon(r)).create((a,b) => \(i,a)),
        "def "+n2l(i)+"Col = "+copy(m=1,r=canon(r)).create((a,b) => \(a,i))
      )) :::
      List(
        "def t = "+copy(r=canon(r)).create((i,j) => \(j,i))
      ) :::
      ( if (!t.rational) Nil
        else if (n==2) List (
          "def inv = { val idet = 1/det; new "+copy(r=canon(r)).classname+"(yy*idet,-xy*idet,-yx*idet,xx*idet) }"
        )
        else if (n==3) List(
          "def inv = { val idet = 1/det; "+copy(r=canon(r)).create((i,j) => Expr("idet*("+det(n-1, after(j,i))+")",t).mathed) + " }"
        )
        else List (
          "def inv = mut.invEq.imm"
        )
      )
    }.getOrElse(Nil)
    
    def matEqMods = onlyIf (isMatrix && r==AssignableType) {
      val row = copy(n=m,m=1)
      val col = copy(n,1)
      (1 to n).toList.flatMap(i => List(
        "class RowView"+n2l(i).toUpperCase+" extends "+row.classname+
          (1 to m).map(j => "def "+n2l(j)+" = "+(\(i,j).text)+"; def "+n2l(j)+"_=(x0: "+t.longfix+") { "+(\(i,j).text)+" = x0 }").mkString(" { ","; "," }"),
        "class ColView"+n2l(i).toUpperCase+" extends "+col.classname+
          (1 to n).map(j => "def "+n2l(j)+" = "+(\(j,i).text)+"; def "+n2l(j)+"_=(x0: "+t.longfix+") { "+(\(j,i).text)+" = x0 }").mkString(" { ","; "," }"),
        "def "+n2l(i)+"RowView = new RowView"+n2l(i).toUpperCase,
        "def "+n2l(i)+"ColView = new ColView"+n2l(i).toUpperCase
      )) :::
      List(
        "def tEq: this.type = { "+(for (i <- 1 to n; j <- i+1 to m) yield (
          " = " + \(i,j).text + "; " + \(i,j).text + " = " + \(j,i).text + "; " + \(j,i).text + " = temp;"
        )).mkString("var temp", " temp"," this }")
      ) :::
      ( if (!t.rational) Nil
        else if (n==2) List(
          "def invEq: this.type = { val idet = 1/det; var temp: "+t.longfix+" = yy; yy = xx*idet; xx = temp*idet; xy = -xy*idet; yx = -yx*idet; this }"
        )
        else if (n==3) List(
          "def invEq: this.type = { val idet = 1/det; " +
            ( for (i <- 1 to n; j <- 1 to m) yield (?(i,j,"val "+_+"0")+" = idet*("+det(2,after(j,i))+"); ") ).mkString +
            ( for (i <- n to 1 by -1; j <- m to 1 by -1) yield (?(i,j) + " = " + ?(i,j,_+"0") + "; ") ).mkString +
            "this }"
        )
        else List(
          "def invEq: this.type = gaussJordanEq("+copy(r=ImmutableType).classname+".identity)"
        )
      )
    }.getOrElse(Nil)
    
    def mulMatVec(e: Entity) = onlyIf(((isVector(e.m) && e.isMatrix) || (isMatrix && e.isVector(n)) || (isMatrix && sameSize(e))) && r==GenericType && r==e.r) {
      val targ = Entity( if (isVector) n else e.n, if (isVector || e.isVector) 1 else m, ^(e), canon(r) )
      val ee = e.withId
      "def **("+e.param+") = "+targ.create((i,j) => {
        if (isVector) Expr((for (k <- 1 to e.m) yield \\(k,j,targ.t).text+"*"+ee.\\(k,i,targ.t).text).mkString("+"),targ.t).mathed
        else Expr((for (k <- 1 to m) yield \\(i,k,targ.t).text+"*"+ee.\\(k,j,targ.t).text).mkString("+"),targ.t).mathed
      })
    }.toList
    def mulMatMatEq(e: Entity) = onlyIf(isMatrix && sameSize(e) && r==AssignableType && e.r==GenericType) {
      val temp = new Entity(n,m,^(e),r) { override def prefixId(s: String) = "val "+s+"0" }
      val ee = e.withId
      "def "+assop("**",e.t)+"("+e.param+") = {" +
      temp.assign((i,j) => Expr((for (k <- 1 to m) yield ?(i,k,nt=(^(e)))+"*"+ee.?(k,j,nt=temp.t)).mkString("+"),temp.t)) + "; " +
      assign((i,j) => Expr(?(i,j,_+"0"),^(e)).mathed.as(t)) + "; " +
      "this }"
    }.toList
    def mulVecMatEq(e: Entity) = onlyIf(e.isMatrix && isVector(e.n) && r==AssignableType && e.r==GenericType) {
      val temp = new Entity(n,m,^(e),r) { override def prefixId(s: String) = "val "+s+"0" }
      val ee = e.withId
      List(
        ("", (i: Int, k: Int) => k, (i: Int, k: Int) => i),
        (":", (i: Int, k: Int) => i, (i: Int, k: Int) => k)
      ).map{ case (s,a,b) =>
        "def "+assop("**",e.t)+s+"("+e.param+") = {" +
        temp.assign((i,j) => Expr((for (k <- 1 to n) yield ?(k,1,nt=(^(e)))+"*"+ee.?(a(i,k),b(i,k),nt=temp.t)).mkString("+"),temp.t)) + "; " +
        assign((i,j) => Expr(?(i,j,_+"0"),^(e)).mathed) + "; " +
        "this }"
      }
    }.getOrElse(Nil)
    def setMatVec(e: Entity) = onlyIf(isMatrix && e.isVector(n) && r==AssignableType && e.r==GenericType) {
      val ee = e.withId
      List(
        ("|", (i: Int, j: Int) => i),
        ("-", (i: Int, j: Int) => j)
      ).map { case (s,a) =>
        "def "+assop(":"+s,e.t)+"("+e.param+") = { " + assign((i,j) => ee\(a(i,j),1)) + "; this }"
      }
    }.getOrElse(Nil)
    /*
    def mulMatVecEq(e: Entity) = onlyIf(
      ((isVector(e.m) && e.isMatrix) || (isMatrix && e.isVector(n)) || (isMatrix && sameSize(e))) && r==AssignableType && r==GenericType
    ) {
      val temp = new Entity(targ.n, targ.m, ^(e), targ.r) { override def prefixId(s: String) = "val "+s+"0" }
      val ee = e.withId
      "def "+assop("**")+"("+e.param+") = { " + 
      temp.assign((i,j) => {
        if (isVector) Expr((for (k <- 1 to e.m) yield \\(k,j,targ.t).text+"*"+ee.\\(k,i,targ.t).text).mkString("+"),targ.t)
        else Expr((for (k <- 1 to m) yield \\(i,k,targ.t).text+"*"+ee.\\(k,j,targ.t).text).mkString("+"),targ.t)
      }) +
      targ.ass
    }.toList
    */
    /* Too clunky.
    def arithMatVec(op: String)(e: Entity) = {
      onlyIf( ((isVector && e.isMatrix && n==e.m) || (isMatrix && e.isVector && n==e.n)) && r==GenericType && e.r==GenericType ) {
        val targ = copy(n max e.n, m max e.m, ^(e), canon(r))
        val ee = e.withId
        for ((meth,vert) <- List( (op+"|"+op,true), (op+"-"+op,false) )) yield {
          "def "+meth+"("+e.param+") = "+targ.create((i,j) => {
            val (a,b,c,d) = (if (isVector) (if (vert) (i,1,i,j) else (j,1,i,j)) else (if (vert) (i,j,i,1) else (i,j,j,1)))
            Expr(\\(a,b,targ.t).text + op + ee.\\(c,d,targ.t).text, targ.t).mathed
          })
        }
      }.getOrElse(Nil)
    }
    */
    
    def ifChains(n: Int, f: Int => String): List[String] = {
      if (n<2) List("{","1","}")
      else if (n==2) List("if (math.abs("+f(1)+") < math.abs("+f(2)+")) {","2","} else {","1","}")
      else {
        (
          ifChains(n-1, i => f(i+1)).zipWithIndex.map(x => if ((x._2 & 0x1)==0x1) (x._1.toInt+1).toString else x._1),
          ifChains(n-1, i => if (i==1) f(1) else f(i+1)).zipWithIndex.map(x => if ((x._2 & 0x1)==0x1 && x._1.toInt > 1) (x._1.toInt+1).toString else x._1)
        ).fold((l,r) =>
          List("if (math.abs("+f(1)+") < math.abs("+f(2)+")) {"+l.head) :::
          l.tail.init :::
          List(l.last + "} else {" + r.head) :::
          r.tail.init :::
          List(r.last+"}")
        )
      }
    }
    
    def rowEsch(n: Int, f: (Int,Int)=>String, m: Int, g: (Int,Int)=>String): String = {
      def T(l: Int) = ("t"+n)+l
      def I(l: Int) = ("i"+n)+l
      def J(l: Int) = ("j"+n)+l
      if (n==1) {
        "{ val "+I(0)+"=1/"+f(1,1)+"; "+f(1,1)+"=1; " +
        (1 to m).map(j => g(1,j)+"*="+I(0)).mkString("; ") +
        " }"
      }
      else {
        ifChains(n, a => f(a,1)).map(x => if (!x.matches("""\d+""")) x else {
          val i = x.toInt
          if (i==1) {
            "val "+I(i)+"=1/"+f(1,1)+"; "+f(1,1)+"=1; " +
            (2 to n).map(j => f(1,j)+"="+f(1,j)+"*"+I(i)+"; ").mkString +
            (1 to m).map(j => g(1,j)+"*="+I(i)).mkString("; ")
          }
          else {
            "val "+I(i)+"=1/"+f(i,1)+"; "+f(i,1)+"="+f(1,1)+"; "+f(1,1)+"=1; var " +
            (2 to n).map(j => T(i)+"="+f(1,j)+"; "+f(1,j)+"="+f(i,j)+"*"+I(i)+"; "+f(i,j)+"="+T(i)+"; ").mkString +
            (1 to m).map(j => T(i)+"="+g(1,j)+"; "+g(1,j)+"="+g(i,j)+"*"+I(i)+"; "+g(i,j)+"="+T(i)).mkString("; ")
          }
        }).mkString + "; " +
        (2 to n).map(i => {
          "{ val "+J(i)+"="+f(i,1)+"/"+f(1,1)+"; "+f(i,1)+"=0; " +
          (2 to n).map(j => f(i,j)+"="+f(i,j)+"-"+f(1,j)+"*"+J(i)+"; ").mkString +
          (1 to m).map(j => g(i,j)+"-="+g(1,j)+"*"+J(i)).mkString("; ") +
          "}"
        }).mkString(if (n>2) "{ " else "", "; ", if (n>2) " }" else "") + "; " +
        rowEsch(n-1, (i,j) => f(i+1,j+1), m, (i,j) => g(i+1,j))
      }
    }
    
    def reduceRE(n: Int, f: (Int,Int)=>String, m: Int, g: (Int,Int)=>String): String = {
      if (n==1) "" else {
        (1 to (n-1)).reverse.map(i=>
          (1 to m).map(j => g(i,j)+"-="+f(i,n)+"*"+g(n,j)+"; ").mkString
        ).mkString +
        reduceRE(n-1, f, m, g)
      }
    }
    
    def matMatMods(e: Entity) = onlyIf (isMatrix && sameSize(e) && r==AssignableType && e.r==GenericType && t==e.t) {
      onlyIf(t.rational) {
        val ee = e.withId
        "def gaussJordanEq("+e.param+"): this.type = {" +
          (for (i <- 1 to n; j <- 1 to m) yield "var "+(?(i,j,_+"0"))+" = "+(ee?(i,j))).mkString("; ")+"; " +
          rowEsch(n, (a,b) => ?(a,b), n, (a,b) => ?(a,b,_+"0")) + "; " +
          reduceRE(n, (a,b) => ?(a,b), n, (a,b) => ?(a,b,_+"0")) + 
          all.map(x => x.text + " = " + x.text + "0").mkString("; ")+"; " +
          "this }"
      }.toList
    }.getOrElse(Nil)
      
    def matOps(es: List[Entity]) = List(
      /* too clunky: arithMatVec("+") _, arithMatVec("-") _, arithMatVec("*") _, arithMatVec("/") _, */
      setMatVec _, mulMatVec _, mulMatMatEq _, mulVecMatEq _, matMatMods _
    ).flatMap(opx => es.map(opx)).flatten
  }
  
  def sho(xss: List[List[String]], i: Int) { xss.filterNot(_.isEmpty).foreach(xs => xs.map(" "*i + _).foreach(println)) }
  
  case class Pieces(
    generator: Entity,
    primary: List[List[String]],
    companion: List[List[String]],
    implicits: List[String]
  ) {
    def showPrimary(ind: Int) { sho(primary,ind) }
    def showCompanion(ind: Int) { if (companion.flatten.length > 2) sho(companion,ind) }
    def showImplicits(ind: Int) { if (implicits.length>0) sho(List(implicits),ind) }
    def showPC(ind: Int) { showPrimary(ind); println; showCompanion(ind); println; println }
  }
  
  def mimic(e: Entity, verb: Boolean, ur: Boolean, ut: Boolean) = {
    val cn = if (verb) e.shortclassname _ else e.longclassname _
    ("type "+cn(ur,ut)+" = "+e.packageclassname) :: (if (e.isScalar) Nil else ("val "+cn(ur,ut)+" = "+e.packageclassname)::Nil)
  }
  def mimicall(e: Entity) = mimic(e,true,true,true)
  def mimict(e: Entity) = mimic(e,true,false,true)
  def mimicsolo(e: Entity) = mimic(e,true,false,false)
  def babbleall(e: Entity) = mimic(e,false,true,true)
  def babblet(e: Entity) = mimic(e,false,false,true)
  def babblesolo(e: Entity) = mimic(e,false,false,false)
  
  
  var veclen = 4
  var matlen = 4
  def main(args: Array[String]) = {
    println("/* Copyright 2011 Rex Kerr and the Howard Hughes Medical Institute, all rights reserved */")
    println("/* This file is covered by the BSD license with HHMI as the organization */")
    println("/* See http://www.opensource.org/licenses/bsd-license.php for licence template text. */")
    
    val in = (s: String) => "  "+s
    matlen = args.find(_.startsWith("-matrix=")).flatMap(_.substring(8).intOption).getOrElse(4)
    veclen = args.find(_.startsWith("-vector=")).flatMap(_.substring(8).intOption).getOrElse(4) max matlen
    val ent = (for (i <- 1 to veclen; j <- Set(1,i).toList.sorted; r <- reps; n <- nums) yield Entity(i,j,n,r)).filter(e => !e.isMatrix || e.n <= matlen).toList
    val pie = ent.filterNot(e => e.isScalar && e.r != GenericType).map(e => Pieces(
      e,
      List(
        List(e.define),
        e.paramethods.map(in),
        e.overmethods.map(in),
        ( for (Entity(nn,mm,nt,rt,_,_) <- ent if (e.n==nn && e.m==mm)) yield e.opSolo(nt,rt).map(in) ).toList.flatten,
        e.uniAll.map(in),
        e.opArith(ent).map(in),
        e.opMakesScalar(ent).map(in),
        e.matMods.map(in),
        e.matOps(ent).map(in),
        e.matEqMods.map(in),
        List(e.enddef())
      ),
      List(
        List(e.defcompanion),
        e.companionmethods.map(in),
        List(e.enddef(ob = true))
      ),
      e.implicitconversions
    ))
    println("package org.kerrlab.shipvl")
    println
    println("package vector {")
    println("  import scalar._")
    println("  import matrix._")
    pie.filter(_.generator.isVector).foreach(_.showPC(2))
    println("}\n\n\n")
    println("package matrix {")
    println("  import scalar._")
    println("  import vector._")
    pie.filter(_.generator.isMatrix).foreach(_.showPC(2))
    println("}\n\n\n")
    println("package scalar {")
    println("  import vector._")
    println("  import matrix._")
    pie.filter(_.generator.isScalar).foreach(_.showPC(2))
    println("}\n\n\n")
    println("package object conversions {")
    println("  import scalar._")
    println("  import vector._")
    println("  import matrix._")
    pie.foreach(p => p.showImplicits(2))
    println("}\n\n\n")
    println("package object ShipVL {")
    ent.filterNot(e => e.isScalar && e.r!=GenericType).flatMap(mimicall).map(in).foreach(println)
    pie.foreach(p => p.showImplicits(2))
    println("}\n\n\n")
    reps.foreach(rt => {
      println("package object "+rt.longfix.toLowerCase+" {")
      ent.filter(e => (e.isScalar && e.r==GenericType) || (!(e.isScalar) && e.r == rt)).flatMap(mimict).map(in).foreach(println)
      nums.foreach(nt => {
        println
        println("  object "+nt.longfix.toLowerCase+"type {")
        ent.filter(e => ((e.isScalar && e.r==GenericType) || (!(e.isScalar) && e.r==rt)) && e.t==nt).flatMap(mimicsolo).map("    "+_).foreach(println)
        println("  }")
      })
      println("}\n\n\n")
    })
    println("package object verbose {")
    ent.filterNot(e => e.isScalar && e.r!=GenericType).flatMap(babbleall).map(in).foreach(println)
    reps.foreach(rt => {
      println("  object "+rt.longfix.toLowerCase+" {")
      ent.filter(e => (e.isScalar && e.r==GenericType) || (!(e.isScalar) && e.r == rt)).flatMap(babblet).map("    "+_).foreach(println)
      nums.foreach(nt => {
        println
        println("    object "+nt.longfix.toLowerCase+"type {")
        ent.filter(e => ((e.isScalar && e.r==GenericType) || (!(e.isScalar) && e.r==rt)) && e.t==nt).flatMap(babblesolo).map("      "+_).foreach(println)
        println("    }")
      })
      println("  }\n")
    })
    println("}")
  }
}
